home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-05-03 | 45.9 KB | 1,083 lines |
-
-
-
- RS232.C User Documentation
- copyright C. Karcher 1992,93
-
-
-
- > About RS232.C:
-
- RS232.C was written to provide all of the basic functionality needed
- to employ serial I/O in any application written with Borland 'C'
- language compilers. Some features are:
-
- 1. Ease of use. No assembly language or library files are used and a
- simple "#include" statement is all that is required to access all of
- the functions provided.
-
- 2. Both input and output are buffered and interrupt driven for
- efficiency.
-
- 3. Serial ports 1 - 4 are supported on PC, AT and PS/2 compatibles.
- Chained interrupts used on port 3 and 4 are allowed for so as not to
- interfere with devices such as a mouse or printer. Transmission
- speeds of 110 to 115200 baud are available.
-
- 4. Detection and utilization of hardware buffered UARTs (NS16550AF
- etc.) found in some machines is automatic.
-
- 5. Interrupt driven hardware and XON/XOFF flow control is provided for.
-
- 6. All source code is included. RS232.C can be used with all memory
- models.
-
-
- This is user supported software and may be distributed freely in a
- whole and unmodified state. It may be used in part or wholly, free of
- royalty, to develop any commercial applications provided that the
- developer has registered RS232.C. Registration entitles the developer
- to any future enhancements or upgrades which may be released.
- Registration also includes technical support as far as can be provided
- via mail, telephone or electronic messaging. To register RS232.C,
- send a check or money order for $20.00 to:
-
- Chris A. Karcher
- 9537 Evanston Ave. N.
- Seattle WA 98103-3131
-
- The author may be contacted by mail at the above address, by telephone
- at (206)789-7945 or via Compuserve EMAIL to user ID 76406,536.
-
- The author of RS232.C makes no expressed or implied warranty of any
- kind with regard to the software or accompanying documentation. In no
- event shall the author be held liable for incidental or consequential
- damages in connection or arising out of performance or use of any part
- of this software.
-
- The distribution should include the following files:
-
- RS232.C - serial communication routines source code
- RS232.DOC - documentation (this file)
- RS_DEMO.C - sample program demonstrating RS232.C
-
- > Why use RS232.C?
-
- Borland's C compilers (and probably all DOS based C compilers) do
- provide some basic support for serial communications but that support
- makes use of calls to BIOS routines only. The BIOS serial routines
- are adequate only for the slowest of transmission speeds and do not
- exploit all of the capabilities of the hardware found in PC's. If
- efficient serial communication is required, the programmer must
- provide all of the low level software. RS232.C does just that by
- taking care of all the low level details and providing the programmer
- with all the high level functions needed for efficient serial I/O.
-
- > How to use RS232.C:
-
- All that is required to use RS232.C is to include it in your source
- code. The simplest way to do that is with a compiler '#include'
- directive, treating RS232.C just as you would a header file. Having
- done that, three basic steps are all that are required to perform
- serial I/0:
-
- 1. Allocate memory for input and output buffers. (It can be pre-
- allocated at compile time or allocated via some memory
- allocation function like malloc at runtime.)
-
- 2. "Open" the port with the function rs_initport, using the
- desired communication parameters.
-
- perform I/O with functions provided
- . . . . .
- . . . . .
- . . . . .
-
- 3. "Close" the port when finished performing I/O with the
- function rs_close before ending the program.
-
- The file RS_DEMO.C, included with the distribution, is a sample
- terminal program demonstrating most of the functions available with
- RS232.C.
-
- The following code demonstrates an extremely simple but functional
- program which turns a PC into a dumb terminal capable of communicating
- with a modem or another computer:
-
- /*
- TERM: a light weight dumb terminal program to demonstrate rs232.c
- C. Karcher
- */
-
- #include<conio.h>
- #include"src\rs232.c" /* include rs232 variables and functions */
-
- main()
- {
-
- int key = 0;
- char input_buffer[1024],output_buffer[1024]; /* allocate buffers */
-
- /* open COM port 1 with 2400 baud, no parity, 8 data bits, 1 stop bit,
- a 1024 byte input buffer and a 1024 byte output buffer */
-
- if(rs_initport(RS_PORT1,RS_B2400,RS_NOPAR,RS_DBIT8,RS_SBIT1,
- 1024U,input_buffer,1024U,output_buffer) > 0)
- cprintf("Terminal mode active - press escape to exit\r\n");
- else{
- cprintf("Unable to open COM port\r\n");
- return 0;
- }
-
- /* turn on Data Terminal Ready */
- rs_modctrl(RS_WRTMCR,RS_MCRDTR,RS_LINON);
-
- /* display any characters received and send any keystrokes typed until
- escape is pressed */
- do{
- if(rs_inrcvd()) /* if a character has been received */
- putch(rs_getbyt()); /* display it */
- if(rs_keyhit()) /* if a key has been pressed */
- rs_sndbyt((key = getch())); /* send it */
- }while(key != 27); /* if the key pressed was ESC, end */
-
- /* turn off Data Terminal Ready */
- rs_modctrl(RS_WRTMCR,RS_MCRDTR,RS_LINOFF);
-
- /* close the port and exit */
- rs_close();
- return 0;
-
- } /* end TERM */
-
- > RS232.C Functions:
-
- The following is a list of the functions contained in RS232.C and a
- brief explanation of the purpose of each one. A detailed description
- of each function and it's use follows:
-
- rs_initport - Prepares a COM port for use.
- rs_sndbyt - Sends a single byte.
- rs_sndstr - Sends a string of bytes.
- rs_getbyt - Gets a single byte.
- rs_getstr - Gets a string of bytes.
- rs_inrcvd - Returns number of bytes which been received.
- rs_outfre - Returns amount of free space remaining in output buffer.
- rs_error - Returns code for last error detected.
- rs_modctrl - Controls or returns status of modem control lines.
- rs_break - Sends break to remote equipment.
- rs_clrout - Clears output buffer.
- rs_clrin - Clears input buffer.
- rs_keyhit - Determines if a key has been pressed.
- rs_timer - Times events or operations.
- rs_setflow - Establishes or returns status of flow control.
- rs_close - "Closes" port prepared by rs_initport.
-
- > Function Reference: (Note: The pre defined constants shown in
- parentheses with some of the function argument
- descriptions or return value descriptions are
- defined in RS232.C and may be used for
- convenience.)
-
- > rs_initport:
-
- > Purpose:
- Prepares a COM port for use.
-
- > Prototype:
- int rs_initport(char port, long baud, char parity, char data_bits,
- char stop_bits, unsigned in_buf_size, char *in_buf,
- unsigned out_buf_size, char *out_buf);
-
- > Arguments:
- port: Character indicating which port to use. May be one of
- the following:
- '1' (RS_PORT1) - COM port 1
- '2' (RS_PORT2)
- '3' (RS_PORT3)
- '4' (RS_PORT4) - COM port 4
- baud: The long value indicating the baud rate. Can be any of
- the following:
- 110L (RS_B110) - 110 baud
- 300L (RS_B300)
- 600L (RS_B600)
- 1200L (RS_B1200)
- 2400L (RS_B2400)
- 4800L (RS_B4800)
- 9600L (RS_B9600)
- 19200L (RS_B19K)
- 38400L (RS_B38K)
- 57600L (RS_B57K)
- 115200L (RS_B115K) - 115200 baud
- parity: A character indicating the type of parity checking to use
- as follows:
- 'N' (RS_NOPAR) - no parity
- 'E' (RS_EVPAR) - even parity
- 'O' (RS_ODPAR) - odd parity
- 'S' (RS_SPPAR) - parity bit always space
- 'M' (RS_MKPAR) - parity bit always mark
- data_bits: A character indicating how many data bits to use in each
- byte:
- '8' (RS_DBIT8) - 8 data bits
- '7' (RS_DBIT7) - 7 data bits
- stop_bits: A character indicating the number of stop bits to use:
- '1' (RS_SBIT1) - 1 stop bit
- '2' (RS_SBIT2) - 2 stop bits
- in_buf_siz: An unsigned integer between 2 and 32768 indicating the
- size in bytes of the input buffer. The value must be a
- power of 2 (i.e. 2,4,8,16,256,512,1024 etc.).
- in_buf: Character pointer to the input buffer allocated by the
- application.
- out_buf_siz: The unsigned value between 2 and 32768 indicating the
- size in bytes of the output buffer. The value must be a
- power of 2 (i.e. 2,4,8,16,256,512,1024 etc.).
- out_buf: Character pointer to the output buffer.
-
- Note: If RS_POLLED_XMIT is defined, the output buffer is not used
- for serial output. In this case the output buffer size may be 0
- and the output buffer pointer may be NULL.
-
- > Return value:
- rs_initport can return the following integer values indicating
- the success or failure of the operation:
- 4 (RS_UART4) Success, UART detected was a 16550 capable of
- hardware buffering. This capability is automatically
- utilized.
- 3 (RS_UART3) Success, UART detected was a 16550 incapable of
- hardware buffering.
- 2 (RS_UART2) Success, UART detected was an 8250A or 16450.
- 1 (RS_UART1) Success, UART detected was an 8250B
- 0 (RS_NOUART) Failure, no UART detected.
- -1 (RS_BADIBUF) Failure, input buffer size invalid or input
- buffer pointer is a NULL pointer.
- -2 (RS_BADOBUF) Failure, output buffer size invalid or output
- buffer pointer is a NULL pointer.
- -3 (RS_BADPORT) Failure, invalid port argument.
- -4 (RS_BADPAR) Failure, invalid parity argument.
- -5 (RS_BADDBIT) Failure, invalid data_bits argument.
- -6 (RS_BADSBIT) Failure, invalid stop_bits argument.
- -7 (RS_BADBAUD) Failure, invalid baud argument.
-
- > Examples:
-
- if(rs_initport(RS_PORT1,RS_B2400,RS_NOPAR,RS_DBIT8,RS_SBIT1,
- 1024U,input_buffer,1024U,output_buffer) > 0)
- cprintf("Terminal mode active - press escape to exit\r\n");
-
- port_ok = rs_initport(port,RS_B2400,RS_NOPAR,RS_DBIT8,RS_SBIT1,
- BUF_SIZ,in_buf,BUF_SIZ,out_buf);
-
- > Notes:
-
- If a port has already been "opened" with rs_initport, it will
- automatically be closed before another port (or the same port) is
- opened. Only one port may be opened at any one time although the
- interrupt service routine installed by rs_initport can coexist
- with others sharing the same IRQ (interrupt request). An example
- of this would be serial mouse installed on COM port 1 and an
- application using rs_initport to prepare access to COM port 3.
- In this case, both the mouse driver and the application would be
- using IRQ4. Some serial applications "take over" the interrupt
- completely which would disable use of the mouse. The interrupt
- service routine installed by rs_initport "chains" the interrupt
- so that if it is invoked by other than the targeted device,
- control is passed to the appropriate routine.
-
- To determine the appropriate I/O address for a given COM port's
- UART, rs_initport first uses the BIOS data area and, failing
- this, uses the "standard" I/O addresses. Standard IRQ's are also
- assumed. If a serial adapter has been installed with other than
- the standard I/O addresses and IRQ's, rs_initport will not find
- the UART and will fail. The standard addresses and IRQ's assumed
- are:
- port I/O address IRQ
- COM1 3F8 4
- COM2 2F8 3
- COM3 3E8 (PS/2 = 3220) 4 (PS/2 = 3)
- COM4 2E8 (PS/2 = 3228) 3 (PS/2 = 4)
-
- If a UART capable of FIFO buffering is detected, this feature is
- automatically enabled. This can greatly reduce the amount of
- overhead due to interrupts and can prevent "lost characters" due
- to receive overruns at higher transmission speeds.
-
- rs_initport does not alter the state of any of the modem control
- output lines (DTR or RTS). They must be explicitly controlled
- using rs_modctrl.
-
-
- > rs_sndbyt
- > Purpose:
- Sends a single byte.
-
- > Prototype:
- int rs_sndbyt(int rs_snd);
-
- > Arguments:
- The byte to send (int converted to char).
-
- > Return Value:
- Returns 0 if the byte was successfully written to the output
- buffer. Returns -1 no port open or byte could not be written.
-
- > Examples:
-
- if(rs_sndbyt('A'))
- printf("Unable to send message\n");
-
- rs_sndbyt(getche());
-
- > Notes:
- rs_sndbyt writes the character into the output buffer and
- returns immediately without waiting for the character to
- actually be transmitted. If there is no room in the output
- buffer, rs_sndbyt will wait until there is room unless output is
- currently disabled via flow control. In this case, rs_sndbyt
- returns -1 to indicate failure.
- If transmit interrupts are disabled by defining RS_POLLED_XMIT,
- the buffer is not used and the character is sent when the UART
- is free to send it. If output is disabled via flow control,
- the character is not sent and rs_sndbyt returns -1.
-
- > rs_sndstr
-
- > Purpose:
- Sends a string of bytes.
-
- > Prototype:
- int rs_sndstr(int rs_sndcnt, char *rs_sndstr);
-
- > Arguments:
- rs_sndcnt indicates the number of bytes to be sent - if a number
- other than 0 is specified, the string need not be nul
- terminated. If rs_sndcnt is 0, rs_sndstr is treated as a nul
- terminated string and characters will be sent up to but not
- including the terminating nul. rs_sndstr is a character pointer
- to the string to be sent. rs_sndstr can be larger than the size
- of the output buffer and up to 32767 bytes.
-
- > Return Value:
- Function returns 0 on success. If no port is open, it returns
- -1. If the entire string could not be written to the output
- buffer, the number of characters written is returned.
-
- > Examples:
-
- rs_sndstr(0, "Hello, world.");
-
- if(rs_sndstr(strlen(msg),msg)
- printf("Message could not be sent\n");
-
- > Notes:
- Like rs_sndbyt, rs_sndstr writes the characters to the output
- buffer and returns immediately - it does not wait for the
- characters to actually be transmitted. If there is insufficient
- room in the output buffer, it waits until there is unless output
- is currently disabled via flow control. In this case, rs_sndbyt
- returns the number of characters that were written to the
- buffer.
- If transmit interrupts are disabled by defining RS_POLLED_XMIT,
- the output buffer is not used and the function does not return
- until the entire string has been sent or output becomes disabled
- via flow control.
-
- > rs_getbyt
-
- > Purpose:
- Gets a single byte.
-
- > Prototype:
- int rs_getbyt(void);
-
- > Arguments:
- none
-
- > Return Value:
- Returns the next received byte (converted to int) available. If
- no characters are currently waiting to be read from the input
- buffer, returns -1.
-
- > Examples:
-
- while(rs_inrcvd()
- putch(rs_getbyt());
-
- if((ch = rs_getbyt()) < 0)
- printf("No received character\n");
-
- > Notes:
- rs_getbyt does not wait for incoming characters - it returns -1
- immediately if no received characters are available.
-
- > rs_getstr
-
- > Purpose:
- Gets a string of bytes.
-
- > Prototype:
- int rs_getstr(int rs_getcnt, char *rs_getbuf);
-
- > Arguments:
- rs_getcnt is the number of characters to be read from the input
- buffer. If rs_getcnt is 0, characters are read from the input
- buffer until a nul character is encountered, which is included
- in rs_getbuf. rs_getbuf is a character pointer to the string
- which the received characters will be written in to. rs_getbuf
- must be large enough to hold the entire string plus a
- terminating nul character. rs_getstr can be used to get a
- string up to the input buffer size.
-
- > Return Value:
- Returns -1 if no port is open or the number of characters read
- from the input buffer and written to the target string.
-
- > Examples:
-
- if(rs_getstr(8,msg) > 8)
- printf("No message available\n");
-
- > Notes:
- If the requested number of characters are not available,
- rs_getstr does not wait for them to become available but instead
- copies as many as are available and returns the number copied.
-
- > rs_inrcvd
-
- > Purpose:
- Returns number of bytes which been received.
-
- > Prototype:
- unsigned rs_inrcvd(void);
-
- > Arguments:
- none
-
- > Return Value:
- Returns the number of characters which have been received and
- are ready to be read from the input buffer by rs_getbyt or
- rs_getstr.
-
- > Examples:
-
- in_cnt = rs_inrcvd();
-
- rs_getstr(rs_inrcvd(),msg);
-
- > Notes:
- rs_inrcvd will never return a value greater than the input
- buffer size specified when rs_initport was called, even if an
- input buffer overflow has occurred. Use rs_error to determine
- if there has been an overflow.
-
- > rs_outfre
-
- > Purpose:
- Returns amount of free space remaining in output buffer. If
- RS_POLLED_XMIT is defined, this function will not return a
- meaningful value.
-
- > Prototype:
- unsigned rs_outfre(void);
-
- > Arguments:
- none
-
- > Return Value:
- Unsigned integer indicating current amount of free space in
- bytes that are available in the output buffer.
-
- > Examples:
-
- if(rs_outfre() == 0)
- printf("Serial port busy - try again later\n");
-
- printf("%u bytes are currently buffered for output\n",
- output_buffer_size - rs_outfre());
-
- > rs_error
-
- > Purpose:
- Returns code for last error detected.
-
- > Prototype:
- int rs_error(void);
-
- > Arguments:
- none
-
- > Return Value:
- Returns integer whose bit pattern indicates the last error which
- occurred as follows:
- bit 0 (RS_RBER): 1 = receive buffer overflow
- bit 1 (RS_ROER): 1 = receive data overrun (UART received data
- faster than it could be read by the interrupt
- service routine)
- bit 2 (RS_PERR): 1 = parity error
- bit 3 (RS_FERR): 1 = framing error
- bit 4 (RS_BKDT): 1 = break detected
- Returns 0 if no error has been detected since last call to
- rs_error or since port was opened.
-
- > Examples:
-
- if(rs_error())
- printf("Serial port error\n");
-
- if(rs_error() & RS_PERR)
- printf("Parity error!\n");
-
- > Notes:
- A call to rs_error returns a code for any errors which have
- occurred and clears the error flags. Subsequent calls will not
- indicate the same error unless it has reoccurred.
-
- Most UARTs will indicate a framing error when a break is
- detected.
-
- > rs_modctrl
-
- > Purpose:
- Controls or returns status of modem control lines.
-
- > Prototype:
- int rs_modctrl(int rs_cmd,...);
-
- > Arguments:
- rs_cmd = 2 (RS_GETMCR): Return status of modem control output
- lines.
- rs_cmd = 1 (RS_WRTMCR): Change status of modem control output
- lines. 2 additional arguments are required with this
- command. The first is an integer with a bit pattern
- indicating which line(s) to control:
- 1 (RS_MCRDTR): Data Terminal Ready line.
- 2 (RS_MCRRTS): Ready To Send line.
- The second is an integer determining whether the
- line(s) should be turned on or off:
- 0 (RS_LINOFF): Turn line off.
- 1 (RS_LINON): Turn line on.
- rs_cmd = 0 (RS_GETMSR): Return status of modem control input
- lines.
-
- > Return Value:
- If rs_cmd = 0 (RS_GETMSR), an integer with a bit pattern
- indicating the requested status is returned:
- 0x01 (RS_CTSCHG) CTS line changed states
- 0x02 (RS_DSRCHG) DSR line changed states
- 0x04 (RS_RICHG) RI line changed states
- 0x08 (RS_DCDCHG) DCD line changed states
- 0x10 (RS_CTSSTE) state of CTS line
- 0x20 (RS_DSRSTE) state of DSR line
- 0x40 (RS_RICHG) state of RI line
- 0x80 (RS_DCDCHG) state of DCD line
- If rs_cmd = 1 (RS_WRTMCR), zero is returned.
- If rs_cmd = 2 (RS_GETMCR), an integer with a bit pattern
- indicating the current state of the modem control output lines
- is returned:
- 0x01 (RS_MCRDTR): 1 = DTR on
- 0x02 (RS_MCRRTS): 1 = RTS on
- If an operation fails (no port open), -1 is returned.
-
- > Examples:
-
- if(! (rs_modctrl(RS_GETMSR) & RS_MSRDSR))
- printf("Remote equipment is not ready\n");
-
- /* set Data Terminal Ready output line on */
- rs_modctrl(RS_WRTMCR,RS_MCRDTR,RS_LINON);
-
- if(rs_modctrl(RS_GETMCR) & RS_MCRRTS)
- printf("Ready To Send output line is currently off\n");
-
- > Notes:
- When using rs_modctrl to return the status of the modem status
- input lines (rs_cmd = RS_GETMSR), the lower 4 bits of the value
- returned indicate that the corresponding modem status lines have
- changed states since the last time the status was read.
- The next time the status is read, these bits will be cleared
- unless a line has again changed states.
-
- When using rs_modctrl to set modem control output lines, more
- than 1 line can be turned on or off with the same call by or'ing
- the bit pattern for the desired lines (i.e.
- rs_modctrl(WRTMCR,RS_MCRDTR | RS_MCRRTS,RS_LINON) ).
-
- > rs_break
-
- > Purpose:
- Sends break to remote equipment.
-
- > Prototype:
- int rs_break(void);
-
- > Return Value:
- Returns 0 on success, -1 if no port open.
-
- > Examples:
-
- if(rs_error())
- rs_break();
-
- > Notes:
- An RS232 line is normally in the "marking" state when idle.
- rs_break places the line in a spacing state for approx. 1
- character period. It is used by some communications equipment
- to signal some event.
-
- If a buffer transmit is currently in progress, rs_break will
- wait until the buffer is empty before sending the break unless
- output is currently disabled via flow control. In this case,
- rs_break will send the break immediately.
-
- > rs_clrout
-
- > Purpose:
- Clears output buffer (and the UART output FIFO if used). If
- RS_POLLED_XMIT is defined, clears the output FIFO only.
-
- > Prototype:
- void rs_clrout(void);
-
- > Return Value:
- none
-
- > Examples:
-
- if(rs_error() & RS_BKDT) /* break detected */
- rs_clrout(); /* stop transmission, clear output */
-
- > Notes:
- rs_clrout will immediately stop transmission and clear the
- output buffer. It has no effect if a port is not open.
-
- > rs_clrin
-
- > Purpose:
- Clears input buffer.
-
- > Prototype:
- void rs_clrin(void);
-
- > Return Value:
- none
-
- > Examples:
-
- if(rs_error()){ /* if an error occurred */
- rs_clrin(); /* clear input buffer */
- rs_sndstr(0,"resend last message");
- }
-
- > Notes:
- rs_clrin will immediately clear the input buffer of any
- characters not yet read. If a UART FIFO is in use, it will also
- be cleared.
-
- > rs_keyhit
-
- > Purpose:
- Determines if a key has been pressed.
-
- > Prototype:
- int rs_keyhit(void);
-
- > Return Value:
- Returns 0 if no keys have been pressed, 1 if one or more keys
- have been pressed but not yet read.
-
- > Examples:
-
- if(rs_keyhit())
- rs_sndbyt(getch());
-
- > Notes:
- When constant keyboard monitoring is required, rs_keyhit should
- be used in place of library functions like kbhit(). rs_keyhit
- reads the BIOS keyboard data area directly to determine if any
- keys have been pressed. Library functions like kbhit() call a
- BIOS interrupt routine to accomplish the same thing. This can
- cause loss of incoming serial data when using high transmission
- speeds. A port need not be open to use this function.
-
- > rs_timer
-
- > Purpose:
- Times events or operations.
-
- > Prototype:
- unsigned rs_timer(int rs_cmd);
-
- > Arguments:
- One integer argument determines the action which will be taken:
- 0 (RS_CLRTIM): The current timer value is returned and the
- timer is set to 0.
- 1 (RS_GETTIM): The current timer value is returned and timing
- continues.
-
- > Return Value:
- rs_timer returns an unsigned integer indicating the number of
- "ticks" (18.2 per second) which have elapsed.
-
- > Examples:
-
- rs_timer(RS_CLRTIM);
- rs_sndstr(msg_len,msg);
- while(! rs_inrcvd()){
- if(rs_timer(RS_GETTIM) > 18){ /* wait approx. 1 second */
- printf("Message response timed out\n");
- break;
- }
- }
-
- > Notes:
- rs_timer reads the BIOS time data area directly and avoids use
- of BIOS interrupt calls to do this. It should be used instead
- of library routines such as biostime() to avoid loss of incoming
- serial data when performing high speed communication. The value
- returned may be in error by as much as 1 "tick" so should be
- considered a fairly low resolution timer. A port need not be
- open to use this function.
-
- > rs_setflow
-
- > Purpose:
- Establishes or returns status of flow control.
-
- > Prototype:
- int rs_setflow(int rs_cmd,...);
-
- > Arguments:
- rs_cmd can be one of the following:
- 0 (RS_FLWOFF) - Disables flow control.
- 1 (RS_FLWHDW) - Enables hardware flow control. Requires an
- additional integer argument to define hardware
- line(s) to use for flow control:
- 1 (RS_FLWCTS) - Use Clear To Send
- 2 (RS_FLWDSR) - Use Data Set Ready
- 4 (RS_FLWRI) - Use Ring Indicator
- 8 (RS_FLWDCD) - Use Data Carrier Detect
- 2 (RS_FLWXON) - Enables XON/XOFF flow control. Requires two
- additional parameters, the first of which
- defines the character to use for XON, the
- second defining the character to use as XOFF.
- The following two pre-defined constants are
- the standard XON and XOFF characters:
- 0x11 (RS_XON)
- 0x13 (RS_XOFF)
- 3 (RS_FLWSTAT) - Returns the status of flow control. 1
- indicates that output is currently disabled
- by flow control. 0 indicates output is
- currently enabled.
- 4 (RS_FLWINS) - Inserts a control character in the output
- stream. The control character to be sent is
- defined by one additional argument.
-
-
- > Return Value:
- Returns -1 if no port open. Except when rs_cmd is 3
- (RS_FLWSTAT), returns 0 on success. When rs_cmd is RS_FLWSTAT,
- value returned is status of flow control.
-
- > Examples:
-
- /* set flow control to use Data Set Ready hardware line */
- rs_setflow(RS_FLWHDW,RS_FLWDSR);
-
- /* set flow control to XON/XOFF using standard XON and XOFF
- characters */
- rs_setflow(RS_FLWXON,RS_XON,RS_XOFF);
-
- if(rs_setflow(RS_FLWSTAT))
- printf("Output currently disabled via flow control\n");
-
- /* send XOFF character to remote */
- rs_setflow(RS_FLWINS,RS_XOFF);
-
- /* disable flow control */
- rs_setflow(RS_FLWOFF);
-
- > Notes:
- Enabling flow control, whether with hardware line monitoring or
- with XON/XOFF characters, will cause output to stop immediately
- when the defined condition is met (the selected hardware line
- goes to logic 0 or the character used as XOFF is received). Any
- characters buffered for output will remain buffered and
- transmission will resume when the hardware line being used for
- flow control goes to logic 1 or the character used for XON is
- received. Flow control effects output only. Characters can
- continue to be received or written to the output buffer up to
- it's capacity when output is disabled via flow control. Only
- one type of flow control may be used at once.
-
- If the remote equipment is using XON/XOFF type flow control,
- rs_setflow can be used to control remote output with the
- RS_FLWINS command. The control character will be sent
- immediately, even if characters are already being transmitted
- via the output buffer. The buffer will continue to be sent
- after the control character has been transmitted.
-
- > rs_close
-
- > Purpose:
- Closes" port prepared by rs_initport.
-
- > Prototype:
- void rs_close(void);
-
- > Return Value:
- none
-
- > Notes:
- This function should always be used when an application has
- "opened" a port with rs_initport. rs_close disables interrupts
- from the port and restores the previous interrupt vector.
- Failure to call rs_close before exiting from the application can
- very likely cause undefined behavior. The function can be
- registered with the library function atexit() to insure that it
- gets called. It is probably also a good idea to install a
- ctrl-break handler which calls rs_close to prevent exiting the
- application without closing the port:
-
- int c_break(void)
- {
- rs_close();
- return(0);
- }
-
- /* somewhere in application */
- ctrlbrk(c_break); /* library function ctrlbrk registers
- cleanup function */
-
- If a buffer transmit is currently in progress, rs_close waits
- for it to finish before closing the port unless output is
- currently disabled via flow control.
-
- > General Notes:
-
- > Baud Rates:
- Although RS232.C supports speeds up to 115.2K baud, the higher
- speeds may not work on some machines due to a variety of
- factors. Foremost is the speed of the CPU itself - slower
- machines may not be capable of sustained input at high speeds.
- Other factors may effect the maximum usable baud rate also. For
- instance, even though a machine's CPU is fast enough to support
- high speeds, other software running on the machine may prevent
- it. (One example is a 386 CPU running an extended memory
- manager which places the CPU in protected mode. This can cause
- interrupt service routines to be slower.) The BIOS in some
- machines may have routines which are too slow, such as the timer
- interrupt service routine - this can effect time critical
- routines like the serial interrupt service routine in RS232.C.
- Only experimentation can determine what the highest usable speed
- is on any given machine and configuration.
-
- > Interrupts:
- Because machines equipped with COM ports 3 and 4 use hardware
- interrupt lines shared with ports 1 and 2, a problem can arise
- with some hardware. For example, an internal modem is installed
- as COM1 and you are attempting to use COM3 for some other
- purpose. Because both ports use interrupt request line IRQ4, if
- the modem board is "holding" IRQ4 inactive, COM3 will never be
- able to signal an interrupt and thus will appear dead. Most
- serial adapter boards do not behave this way and will "float"
- the interrupt request line when not signaling an interrupt.
- Also, the problem can only occur if software accessing the
- problem board leaves interrupts enabled at the board.
- (Specifically, the signal OUT2 is left active with modem control
- register bit 3.)
-
- If your application installs any of it's own interrupt routines,
- they must re-enable interrupts as soon as possible after entry
- to prevent loss of incoming serial characters.
-
- RS232.C re-prioritizes hardware interrupts to give serial
- interrupts the highest priority. While this will have no
- noticeable effect with most applications, it should be taken
- into account in those applications which make use of hardware
- interrupts from other sources.
-
- > Polled vs. interrupt driven output:
- The default method of character transmission by RS232.C uses
- interrupts. Each time a character is written a character is
- written to the UART's transmit holding register, an interrupt is
- generated by the UART when the transmit holding register becomes
- available for the next character to be sent. It may be
- desirable under certain circumstances to disable this feature
- and use "polled transmission" instead. This requires that
- software constantly poll the UART and write the next character
- when the transmit holding register becomes free. The method
- used is determined at compile time. If the macro RS_POLLED_XMIT
- is defined, the polling method will be used. The following line
- placed at the beginning of the application's source code will
- accomplish this:
-
- #DEFINE RS_POLLED_XMIT
-
- The following describes some advantages and disadvantages to
- both methods:
-
- Interrupt driven output:
- Advantages: This is the more efficient method of output. No
- CPU time is wasted polling the UART. Characters may be queued
- for output and the application may go on processing without
- waiting for the characters to actually be sent.
-
- Disadvantages: Some additional overhead is incurred in the
- interrupt service routine. At high transmission speeds this
- may cause problems on slower machines or under certain
- circumstances. Interrupt driven output may not work properly
- with some non-standard hardware.
-
- Polled output:
- Advantages: This method makes the interrupt service routine
- slightly "leaner" and simplifies some of the code elsewhere
- making the overall code size slightly smaller. This method
- may prove to be more reliable on some hardware.
-
- Disadvantages: While the application is sending characters,
- it can do nothing else and no output buffer is used. This
- method is less efficient for throughput.
-
- > Flow control and handshaking:
- Flow control is provided for only in respect to stopping and
- starting output as requested by remote equipment. To utilize
- flow control on remote equipment, the application must monitor
- the conditions requiring flow control (i.e. input buffer free
- space) and take the appropriate action when needed (like sending
- XOFF or dropping a hardware line).
-
- Handshaking is something that is completely arbitrary and the
- only requirement is that the local and remote equipment must
- agree on the protocol. RS232.C provides a function for
- determining the status of and controlling hardware handshake
- lines but it is up to the application to determine how it should
- be used.
-
- > Compiling:
- If RS232.C can be simply included (whether by "pasting" it
- directly into an applications source code or with a preprocessor
- "#include" directive) no special compiler considerations are
- needed. If desired, RS232.C can be split into separate header
- and code files to follow more conventional 'C' conventions.
- This is accomplished by splitting the file where indicated.
- Programs using RS232.C can be compiled and linked from the IDE
- if desired.
-
- Borland's newer compilers have many optimization options and, so
- far, none have caused problems with RS232.C. One necessity is a
- must when using Turbo C 2.0 or earlier compilers: Stack checking
- must be disabled. With stack checking enabled in the older
- compilers, stack checking code is generated even in interrupt
- functions.
-
- Any of the memory models can be used with RS232.C - the only
- drawback is the extra function call overhead and/or memory
- access overhead incurred with the large models. This may
- prevent an application from being able to use the higher baud
- rates on some machines.
-
- > Other Compilers:
-
- RS232.C will compile and run with other DOS based compilers with
- minor changes. The following lists types/functions/macros which
- may need attention under compilers other than those produced by
- Borland:
-
- function type interrupt - must take care of saving and
- restoring registers and provide IRET instruction
-
- unsigned char inportb(int portid) /* reads a byte from
- a hardware port */
-
- void outportb(int portid,unsigned char value) /* writes a
- /* byte to a port */
-
- void far *MK_FP(unsigned seg, unsigned ofs) /* takes integer
- segment and offset
- arguments and returns
- far pointer */
-
-
- > 16550 FIFO mode:
- The 8250A/B and 16450 UARTs found in most machines contain only
- a one byte buffer for input and output. This means that for
- every byte which is received, an interrupt is generated which
- invokes a routine to retrieve the incoming byte and store it.
- This must take place before the next incoming byte has been
- fully received to avoid loss of data. The same is true of
- outgoing data - after each byte being transmitted has been
- sent, an interrupt is generated to invoke a routine to write the
- next byte to the transmit register. In some situations, this is
- simply too much overhead or the interrupt service routines can
- not be allowed to react quickly enough to keep up with the data.
- The more sophisticated 16550 series of UARTs overcome this with
- the inclusion of 16 byte FIFOs (first-in-first-out-registers)
- for both input and output. Software must be able to recognize
- and enable this feature when it's available, and software
- determines how it is used. Code in RS232.C checks for the
- presence of FIFO capable UARTs and automatically employs them.
- Outgoing data is written to the UART at 16 bytes per interrupt
- and incoming data signals generates a receive interrupt after 4
- bytes have accumulated in the FIFO - though up to 16 bytes can
- be received before the interrupt service routine is able to
- respond, without losing any data. These defaults can be changed
- by altering the corresponding constants in the function
- rs_initport (see comments in RS232.C).
-
- > Using More Than 1 COM Port Simultaneously:
- While RS232.C provides no direct support for simultaneous serial
- port access, it is easy enough to accomplish. The solution is
- to include 2 copies of RS232.C in your source code. Because all
- variable and function names in RS232.C begin with 'rs_', this
- makes it easy. To allow an application to access 2 ports at
- once you can do the following:
-
- Create 2 copies of RS232.C renaming them to RS232A.C and
- RS232B.C.
-
- Using Borland's built in editor (or any editor capable of
- search and replace), edit RS232A.C performing a global
- search and replace of the string "rs_" with "rs1_". Do
- the same for "RS_" replacing it with "RS1_".
-
- Do the same thing with RS232B.C, replacing "rs_" with
- "rs2_" and "RS_" with "RS2_".
-
- Include both modified copies in the source code for your
- application. This provides 2 complete sets of functions
- (the function names now start with "rs1_" or "rs2_",
- depending on which port is being accessed) allowing access
- to 2 different ports and simultaneous serial port I/O.
- While not the most elegant of solutions, it is workable
- and RS232.C is small enough so as not to make inclusion of
- 2 copies a problem. (RS232.C adds about 3400 bytes to an
- executable file.)
-
- Only certain ports can be used simultaneously. Because COM1
- uses the same hardware interrupt as COM3 and COM2 uses the same
- interrupt as COM4, you will most likely not be able to use COM1
- with COM3 or COM2 with COM4. The problem arises due to the
- design of the hardware - if a UART has its OUT2 hardware line at
- logic 1 (necessary to enable interrupts), another UART sharing
- the same interrupt will not be able to signal an interrupt.
- This is true of any serial communications software which uses
- interrupts. A serial mouse driver using COM1 preventing serial
- I/O on COM3 is one example of how this problem may arise even
- when using only one port in an application. One way to get
- around this problem (other than opening and closing the ports as
- needed using rs_close) is to turn off OUT2 on the UART which is
- NOT currently being utilized. OUT2 is controlled by bit 3 of
- the modem control register at offset 4 from the UART's base I/O
- address. The following is an example which would allow
- interrupts to occur on COM3 and reenable interrupts on COM1 when
- finished:
-
- outportb(0x3FC,(inportb(0x3FC) & '\xF7'); /* COM1 OUT2 off */
- outportb(0x3EC,(inportb(0x3EC) | '\x08'); /* COM3 OUT2 on */
- perform I/O with COM3
- . . . .
- . . . .
- . . . .
- outportb(0x3EC,(inportb(0x3EC) & '\xF7'); /* COM3 OUT2 off */
- outportb(0x3FC,(inportb(0x3FC) | '\x08'); /* COM1 OUT2 on */
- resume I/O with COM1